package online.yangcloud.web.service.meta.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.update.UpdateChain;
import online.yangcloud.common.annotation.TimeConsuming;
import online.yangcloud.common.common.AppConstants;
import online.yangcloud.common.common.AppProperties;
import online.yangcloud.common.enumration.FileTypeEnum;
import online.yangcloud.common.enumration.YesOrNoEnum;
import online.yangcloud.common.model.business.list.ListSplitBusiness;
import online.yangcloud.common.model.view.PagerView;
import online.yangcloud.common.tools.ExceptionTools;
import online.yangcloud.common.tools.ListTools;
import online.yangcloud.core.mapper.FileMapper;
import online.yangcloud.core.mapper.GlobalMapper;
import online.yangcloud.core.model.entity.FileEntity;
import online.yangcloud.web.service.meta.FileMetadataService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhuboyang
 * @since 2023年06月10 22:15:33
 */
@Service
@Transactional(rollbackFor = Exception.class)
@TimeConsuming
public class FileMetadataServiceImpl implements FileMetadataService {

    @Resource
    private GlobalMapper globalMapper;

    @Override
    public void insertWidthPrimaryKey(FileEntity file) {
        int updateResult = globalMapper.acquireFile().insertWithPk(file);
        if (updateResult == 0) {
            ExceptionTools.businessLogger();
        }
    }

    @Override
    public void batchInsert(List<FileEntity> files) {
        while (ObjUtil.isNotNull(files) && !files.isEmpty()) {
            ListSplitBusiness<FileEntity> business = ListTools.split(files, AppConstants.Batch.COUNT);
            globalMapper.acquireFile().insertBatch(business.getSplits());
            files = business.getSources();
        }
    }

    @Override
    public void batchRemove(List<String> ids, String userId) {
        while (ObjUtil.isNotNull(ids) && !ids.isEmpty()) {
            ListSplitBusiness<String> business = ListTools.split(ids, AppConstants.Batch.COUNT);
            UpdateChain.of(FileEntity.class)
                    .set(FileEntity::getIsDelete, YesOrNoEnum.YES.code())
                    .set(FileEntity::getUploadTime, DateUtil.date().getTime())
                    .where(FileEntity::getId).in(business.getSplits())
                    .update();
            ids = business.getSources();
        }
    }

    @Override
    public void updateById(FileEntity file) {
        int updateResult = globalMapper.acquireFile().update(file);
        if (updateResult == 0) {
            ExceptionTools.businessLogger();
        }
    }

    @Override
    public void batchUpdate(List<FileEntity> files) {
        // 分批次更新
        while (ObjUtil.isNotNull(files) && !files.isEmpty()) {
            ListSplitBusiness<FileEntity> business = ListTools.split(files, AppConstants.Batch.COUNT);
            List<FileEntity> splits = business.getSplits();
            Db.executeBatch(splits, splits.size(), FileMapper.class, (mapper, metadata) -> {
                UpdateChain.of(mapper)
                        .set(FileEntity::getPid, metadata.getPid())
                        .set(FileEntity::getAncestors, metadata.getAncestors())
                        .where(FileEntity::getId).eq(metadata.getId())
                        .update();
            });
            files = business.getSources();
        }
    }

    @Override
    public FileEntity queryById(String id) {
        return globalMapper.acquireFile().selectOneByQuery(QueryWrapper.create()
                .where(FileEntity::getId).eq(id)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public FileEntity queryById(String id, String userId) {
        return globalMapper.acquireFile().selectOneByQuery(QueryWrapper.create()
                .where(FileEntity::getId).eq(id)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public FileEntity queryByPid(String pid, String userId) {
        return globalMapper.acquireFile().selectOneByQuery(QueryWrapper.create()
                .where(FileEntity::getId).eq(pid)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public FileEntity queryByIdInDeleted(String id, String userId) {
        return globalMapper.acquireFile().selectOneByQuery(QueryWrapper.create()
                .where(FileEntity::getId).eq(id)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.YES.code()));
    }

    @Override
    public List<FileEntity> queryListByIds(List<String> fileIds, String userId) {
        return globalMapper.acquireFile().selectListByQuery(QueryWrapper.create()
                .where(FileEntity::getId).in(fileIds)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public List<FileEntity> queryListByPid(String pid, String userId) {
        return globalMapper.acquireFile().selectListByQuery(QueryWrapper.create()
                .where(FileEntity::getPid).eq(pid)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public List<FileEntity> queryDeletedFiles(Integer pageIndex, Integer pageSize, String userId) {
        return globalMapper.acquireFile().selectListByQuery(QueryWrapper.create()
                .where(FileEntity::getIsDelete).eq(YesOrNoEnum.YES.code())
                .and(FileEntity::getUserId).eq(userId)
                .orderBy(FileEntity::getUploadTime).asc()
                .limit((pageIndex - 1) * pageSize, pageSize));
    }

    @Override
    public List<FileEntity> queryChildDirsListByPid(String pid, String userId) {
        return queryChildListByPid(pid, userId, Boolean.TRUE);
    }

    @Override
    public List<FileEntity> queryChildListByPid(String pid, String userId, Boolean isOnlyDir) {
        List<FileEntity> files = globalMapper.acquireFile().selectListByQuery(QueryWrapper.create()
                .where(FileEntity::getPid).eq(pid)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
        List<FileEntity> tmp = new ArrayList<>();
        for (FileEntity file : files) {
            if (!isOnlyDir) {
                if (FileTypeEnum.FILE.is(file.getType())) {
                    tmp.add(file);
                }
            }
            if (FileTypeEnum.DIR.is(file.getType())) {
                tmp.add(file);
                tmp.addAll(queryChildListByPid(file.getId(), userId, isOnlyDir));
            }
        }
        return tmp;
    }

    @Override
    public List<FileEntity> queryLikePrefix(String pid, String fileName, FileTypeEnum type) {
        return globalMapper.acquireFile().selectListByQuery(QueryWrapper.create()
                .where(FileEntity::getPid).eq(pid)
                .and(FileEntity::getName).like(fileName.trim() + AppConstants.Special.PERCENT)
                .and(FileEntity::getType).eq(type.code())
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
    }

    @Override
    public List<FileEntity> queryListAfter(String pid, Long createTime, Integer size, String userId, FileTypeEnum flag) {
        QueryWrapper query = QueryWrapper.create()
                .where(FileEntity::getPid).eq(pid)
                .and(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getCreateTime).gt(createTime)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code())
                .orderBy(FileEntity::getUploadTime).asc()
                .limit(0, size);
        if (ObjectUtil.isNotNull(flag)) {
            query.where(FileEntity::getType).eq(flag.code());
        }
        return globalMapper.acquireFile().selectListByQuery(query);
    }

    @Override
    public PagerView<FileEntity> queryFilesInPager(String pid, String name, String userId, Boolean isOnlyDir,
                                                   Integer pageIndex, Integer pageSize) {
        // 构建 SQL 搜索条件
        QueryWrapper query = QueryWrapper.create()
                .where(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code())
                .and(FileEntity::getUserId).eq(userId)
                .orderBy(FileEntity::getUploadTime).asc()
                .orderBy(FileEntity::getName).asc();
        // 如果要查询的父级文件 id 为 '_'，那说明要查询的是根目录下的
        if (StrUtil.UNDERLINE.equals(pid)) {
            if (StrUtil.isBlank(AppProperties.ROOT_DIR_ID)) {
                FileEntity root = globalMapper.acquireFile().selectOneByQuery(QueryWrapper.create()
                        .where(FileEntity::getPid).eq(CharSequenceUtil.EMPTY)
                        .and(FileEntity::getIsDelete).eq(YesOrNoEnum.NO.code()));
                pid = root.getId();
            } else {
                pid = AppProperties.ROOT_DIR_ID;
            }
        }
        query.where(FileEntity::getPid).eq(pid);
        if (CharSequenceUtil.isNotBlank(name)) {
            query.where(FileEntity::getName).like(name.trim().replaceAll(CharSequenceUtil.EMPTY, AppConstants.Special.PERCENT));
        }
        if (isOnlyDir) {
            query.where(FileEntity::getType).eq(FileTypeEnum.DIR.code());
        }

        // 统计总数据量
        long total = globalMapper.acquireFile().selectCountByQuery(query);

        if (ObjectUtil.isNotNull(pageIndex) && ObjectUtil.isNotNull(pageSize)) {
            query.limit((pageIndex - 1) * pageSize, pageSize);
        }

        // 封装返回数据
        return new PagerView<FileEntity>()
                .setData(globalMapper.acquireFile().selectListByQuery(query))
                .setTotal(total);
    }

    @Override
    public long queryDeletedCount(String userId) {
        return globalMapper.acquireFile().selectCountByQuery(QueryWrapper.create()
                .where(FileEntity::getUserId).eq(userId)
                .and(FileEntity::getIsDelete).eq(YesOrNoEnum.YES.code()));
    }

}
